package org.xlp.base.application.util;

import org.xlp.assertion.AssertUtils;
import org.xlp.encryption.EncryptRSA;
import org.xlp.utils.XLPCharsetUtil;
import org.xlp.utils.io.path.XLPFilePathUtil;

import javax.crypto.Cipher;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * Description: RSA工具加解密
 * <br/>date: 2024/11/13 18:59
 *
 * @version 1.0
 * @author: xlp
 */
public class RSAUtils {
    /**
     * 加密类型
     */
    public static final String ENCRYPTED_TYPE_PADDING = "RSA/ECB/PKCS1Padding";

    /**
     * 加密类型
     */
    public static final String ENCRYPTED_TYPE = "RSA";

    /**
     * 字符编码
     */
    public static final String CHARSET_NAME = XLPCharsetUtil.UTF8;

    /**
     * 秘钥长度
     */
    public static final int PRIVATE_KEY_LEN = 1024;


    /**
     * 加载私钥
     * @param privateKeyPath 私钥文件路径
     * @return 私钥
     * @throws Exception 私钥加载异常，返回该异常
     */
    public static PrivateKey loadPrivateKey(String privateKeyPath) throws Exception {
        synchronized (PrivateKey.class){
            byte[] privateKeyBytes = Files.readAllBytes(Paths.get(privateKeyPath));
            return loadPrivateKey(privateKeyBytes);
        }
    }

    /**
     * 加载私钥
     * @param privateKeyBytes 私钥字节数组
     * @return 私钥
     * @throws Exception 私钥加载异常，返回该异常
     */
    public static PrivateKey loadPrivateKey(byte[] privateKeyBytes) throws Exception {
        synchronized (PrivateKey.class){
            KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPTED_TYPE);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
            return keyFactory.generatePrivate(keySpec);
        }
    }

    /**
     * 加载公钥
     * @param publicKeyPath 公钥文件路径
     * @return 公钥
     * @throws Exception 公钥加载异常，返回该异常
     */
    public static PublicKey loadPublicKey(String publicKeyPath) throws Exception {
        synchronized (PublicKey.class){
            byte[] privateKeyBytes = Files.readAllBytes(Paths.get(publicKeyPath));
            return loadPublicKey(privateKeyBytes);
        }
    }

    /**
     * 加载公钥
     * @param publicKeyBytes 公钥字节数组
     * @return 公钥
     * @throws Exception 公钥加载异常，返回该异常
     */
    public static PublicKey loadPublicKey(byte[] publicKeyBytes) throws Exception {
        synchronized (PublicKey.class){
            // 使用 X509EncodedKeySpec 和 KeyFactory 来生成公钥
            KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPTED_TYPE);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
            return keyFactory.generatePublic(keySpec);
        }
    }

    /**
     * 解密
     * @param cipherText 密文
     * @param privateKey 私钥
     * @return 返回明文
     * @throws Exception 假如解密失败，则抛出该异常
     */
    public static String decrypt(String cipherText, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(ENCRYPTED_TYPE_PADDING);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(cipherText));
        return new String(decryptedBytes, CHARSET_NAME);
    }

    /**
     * 根据私钥生成公钥
     * @param privateKey 私钥
     * @return 公钥
     * @throws Exception 假如公钥生成失败，则抛出该异常
     * @throws IllegalArgumentException 假如公钥不是<code>RSAPrivateKey</code>实例，则抛出该异常
     */
    public static PublicKey generatePublicKeyFromPrivateKey(PrivateKey privateKey) throws Exception {
        if (!(privateKey instanceof RSAPrivateKey)) {
            throw new IllegalArgumentException("The private key is not an instance of RSAPrivateKey.");
        }
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;

        // 从私钥中提取模数和专用指数
        RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(
                rsaPrivateKey.getModulus(), rsaPrivateKey.getPrivateExponent());
        // 使用KeyFactory生成公钥
        KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPTED_TYPE);
        return keyFactory.generatePublic(publicKeySpec);
    }

    /**
     * 生成秘钥文件
     * @param privateKeyFilePath 私钥文件路径
     * @param publicKeyFilePath  共钥文件路径
     * @throws NullPointerException 假如参数为空，则抛出该异常
     * @throws Exception 假如秘钥文件创建失败，则抛出该异常
     */
    public static void createPrivateKeyFile(String privateKeyFilePath, String publicKeyFilePath) throws Exception{
        AssertUtils.isNotNull(privateKeyFilePath, "privateKeyFilePath参数不能为空！");
        AssertUtils.isNotNull(publicKeyFilePath, "publicKeyFilePath参数不能为空！");
        EncryptRSA rsa = new EncryptRSA(PRIVATE_KEY_LEN, CHARSET_NAME);
        privateKeyFilePath = XLPFilePathUtil.normalize(privateKeyFilePath);
        publicKeyFilePath = XLPFilePathUtil.normalize(publicKeyFilePath);
        rsa.privateKeyToFile(new File(privateKeyFilePath));
        rsa.publicKeyToFile(new File(publicKeyFilePath));
    }
}
